package com.kx.yooli.kx.wechat.utils.redis;

import com.kx.yooli.kx.wechat.config.redis.RedisConfig;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author john_sqwen-childe.wen
 * date 2024/4/2
 * PROJECT_NAME wechat
 * description create RedisUtil by john_sqwen
 * and  value is
 */
@Component
public class RedisUtil {
    private final Logger log = LoggerFactory.getLogger(RedisUtil.class);
    @Resource
    private RedisConfig redisConfig;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    
    public void remove(String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    public void removePattern(String pattern) {
        Set<String> keys = this.redisTemplate.keys(pattern);
        assert keys != null;
        if (!keys.isEmpty()) {
            this.redisTemplate.delete(keys);
        }
    }

    public void remove(String key) {
        if (exists(key)) {
            this.redisTemplate.delete(key);
        }
    }

    public boolean exists(String key) {
       // return this.redisTemplate.hasKey(key).booleanValue();
        return Boolean.TRUE.equals(this.redisTemplate.hasKey(key));
    }

    public <T> Object get(String key) {
        ValueOperations<String, Object> operations = this.redisTemplate.opsForValue();
        return  operations.get(key);
    }

    public <T> boolean set(String key, T value) {
        boolean result = false;
        ValueOperations<String, Object> operations = this.redisTemplate.opsForValue();
        operations.set(key, value);
        result = true;
        return result;
    }

    /**
     * 只适用于key对应的值不再更新的问题，如果遇到包含生命周期的值需要更新的情景就不适用了，
     * 因为set 方法会丢失该key的生存时间，变成永久有效的
     * @param key string value
     * @param value object t
     * @param expireTime long value 单位秒
     * @return boolean
     */
    public <T> boolean set(String key, T value, Long expireTime) {
        boolean result = false;
        ValueOperations<String, Object> operations = this.redisTemplate.opsForValue();
        operations.set(key, value);
        this.redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
        result = true;
        return result;
    }

    /**
     *
     * 只适用于key对应的值不再更新的问题，如果遇到包含生命周期的值需要更新的情景就不适用了，
     * 因为set 方法会丢失该key的生存时间，变成永久有效的
     * @param key String value
     * @param value object T
     * @param expireTime long value
     * @param unit timeunit
     * @return boolean
     */
    public <T> boolean set(String key, T value, Long expireTime, final TimeUnit unit) {
        boolean result = false;
       // ValueOperations<Serializable, T> operations = this.redisTemplate.opsForValue();
        ValueOperations<String, Object> operations = this.redisTemplate.opsForValue();
        operations.set(key, value);
        this.redisTemplate.expire(key, expireTime, unit);
        result = true;
        return result;
    }

    /**
     * @param hashName string value
     * @param key      string value
     * @return object T
     */
    public <T> Object getHash(String hashName, String key) {
        Object result = null;
       // HashOperations<Serializable, Serializable, T> operations = this.redisTemplate.opsForHash();
        HashOperations<String, Object, Object> operations = this.redisTemplate.opsForHash();
        //result = operations.get(hashName, key);
        result =   operations.get(hashName, key);
        return result;
    }

    public <T> void setHash(String hashName, String key, T value) {
        redisTemplate.opsForHash().put(hashName, key, value);
    }


    /**
     * 只适用于key对应的值不再更新的问题，如果遇到包含生命周期的值需要更新的情景就不适用了，
     * 因为set 方法会丢失该key的生存时间，变成永久有效的
     * @param key string value
     * @param expireTime value long
     */
    public Boolean expire(String key, Long expireTime) {
        return this.redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
    }

    /**
     * setting synchronized lock
     * @param key value string
     * @param value value final string
     * @return boolean
     */
    public synchronized Boolean setNx(final String key, final String value) throws Exception{
        Object obj = null;
        try {
            obj = redisTemplate.execute((RedisCallback<Object>) connection -> {
                StringRedisSerializer serializer = new StringRedisSerializer();
               // Boolean success = connection.setNX(serializer.serialize(key), serializer.serialize(value));
              //  Boolean success = redisStringCommands.setNX(Objects.requireNonNull(serializer.serialize(key)), Objects.requireNonNull(serializer.serialize(value)));
                RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
                //setting time out time
                Expiration expiration = Expiration.seconds(30);
                //new version  setNx method use it
                Boolean success = new DefaultStringRedisConnection(connection).setNX(Objects.requireNonNull(serializer.serialize(key)), Objects.requireNonNull(serializer.serialize(value)));

                connection.close();
                return success;
            });
        } catch (Exception e) {
            log.error("key : {0} - error {}", key,e);
            throw e;
        }
        return obj != null ? (Boolean) obj : false;
    }

    /**
     *
     * setting synchronized lock over of time
     * @param key string value
     * @param value string value
     * @return boolean
     */
    public synchronized Boolean setNx(final String key, final String value,long timeOut) throws Exception {
        boolean b = this.setNx(key,value);
        redisTemplate.expire(key,timeOut, TimeUnit.SECONDS);
        return b;
    }

    /**
     * 删除锁
     * @param key string value
     * @return void value
     */
    public void unlock(final String key) {
        redisTemplate.delete(key);
    }
}
